package com.hu.common.util.adm;


import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hu.common.domain.dto.FileDTO;
import com.hu.common.enums.MessageEnum;
import com.hu.common.exception.BizException;
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.model.enums.CompressionLevel;
import net.lingala.zip4j.model.enums.CompressionMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.util.Objects;
import java.util.zip.ZipEntry;


/**
 * @author 00355760
 */
public class FileHelper implements AutoCloseable {
    private static final Logger logger = LoggerFactory.getLogger(AutoCloseable.class);
    private static FileHelper FILE_HELPER = null;

    public String readResourceFile(String fileName) throws BizException {
        try {
            InputStream in = Objects.requireNonNull(this.getClass().getClassLoader().getResource(fileName)).openStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            return sb.toString();
        } catch (Exception e) {
            logger.error("readResourceFile exception: {}", E2s.exception2String(e));
            throw new BizException(MessageEnum.EXCEPTION.getMessage());
        }

    }

    public String getCurrentPath() throws IOException {
        File directory = new File("");
        return directory.getCanonicalPath();
    }

    /**
     * 创建唯一Multipart文件存储
     *
     * @param file     源文件
     * @param filePath 文件存储路径
     * @return FileBO
     * @throws IOException 异常信息
     */
    public FileDTO saveUniqueMultipartFile(MultipartFile file, String filePath) throws IOException {
        // 判断路径，没有创建
        File folder = new File(filePath);
        if (!folder.isDirectory()) {
            folder.mkdirs();
        }

        // 对上传的文件重命名，避免文件重名
        String filename = file.getOriginalFilename();
        String extension = getExtension(filename);
        String newFileName = UUIDGeneratorUtil.getSnowflakeId() + extension;
        String path = filePath + newFileName;
        Long fileSize = file.getSize();

        // 存储
        file.transferTo(new File(path));

        FileDTO fileDTO = new FileDTO();
        fileDTO.setName(newFileName);
        fileDTO.setFileExtension(extension);
        fileDTO.setFileSize(fileSize);
        fileDTO.setFilePath(filePath + newFileName);

        return fileDTO;
    }

    /**
     * 可覆盖的Multipart文件存储
     *
     * @param file     源文件
     * @param filePath 文件存储路径
     * @param dbPath   数据库文件路径
     * @return FileBO
     * @throws IOException 异常信息
     */
    public FileDTO saveMultipartFile(MultipartFile file, String filePath, String dbPath) throws IOException {
        // 判断路径，没有创建
        File folder = new File(filePath);
        if (!folder.isDirectory()) {
            folder.mkdirs();
        }

        String filename = file.getOriginalFilename();
        String extension = getExtension(filename);
        Long fileSize = file.getSize();

        // 存储
        file.transferTo(new File(filePath));

        FileDTO fileDTO = new FileDTO();
        fileDTO.setName(filename);
        fileDTO.setFileExtension(extension);
        fileDTO.setFileSize(fileSize);

        if (Assert.notEmpty(dbPath)) {
            fileDTO.setFilePath(dbPath);
        } else {
            fileDTO.setFilePath(filePath);
        }

        return fileDTO;
    }

    public FileDTO saveMultipartFile(MultipartFile file, String filePath) throws IOException {
        return saveMultipartFile(file, filePath, null);
    }

    /**
     * 获取文件拓展名
     *
     * @param fileName 文件名称
     * @return 拓展名
     */
    public String getExtension(String fileName) {
        if (Assert.isEmpty(fileName)) {
            return null;
        }

        int findExtIdx = fileName.lastIndexOf(".");
        return fileName.substring(findExtIdx);
    }

    public String getName(String fileName) {
        if (Assert.isEmpty(fileName)) {
            return null;
        }

        int findExtIdx = fileName.lastIndexOf(".");
        return fileName.substring(0, findExtIdx);
    }

    public void deleteFile(String concat) {
        File file = new File(concat);
        file.delete();
    }

    public long BYTE2MB(Long num) {
        return num / (1024 * 1024);
    }

    public long BYTE2KB(Long num) {
        return num / 1024;
    }

    // 保存文本内容到指定路径下
    public void saveTextFile(String json, String path, String fileName, String extension) throws IOException {
        // 判断路径，没有创建
        File folder = new File(path);
        if (!folder.isDirectory()) {
            folder.mkdirs();
        }

        BufferedWriter writer = null;
        try {
            final String filePath = path + File.separator + fileName + "." + extension;
            writer = new BufferedWriter(new FileWriter(filePath));
            writer.write(json);
        } finally {
            assert writer != null;
            writer.close();
        }
    }

    public void zipFolder(String folderPath, String zipPath) {
        try (ZipFile zipFile = new ZipFile(zipPath)) {
            ZipParameters parameters = new ZipParameters();
            parameters.setCompressionMethod(CompressionMethod.DEFLATE); // 压缩方式
            parameters.setCompressionLevel(CompressionLevel.NORMAL);   // 压缩级别
            zipFile.addFolder(new File(folderPath), parameters);
        } catch (Exception e) {
            logger.info("[zipFolder exception:] {}", E2s.exception2String(e));
            throw new BizException(MessageEnum.IO_EXCEPTION.getMessage());
        }
    }


    /**
     * 解析zip文件
     *
     * @param zipFilePath 文件路径
     * @param unzipPath   解压缩路径
     */
    public void unZipFile(String zipFilePath, String unzipPath) throws Exception {
        // 获取Zip文件
        try (ZipFile zipFile = new ZipFile(zipFilePath)) {
            // 指定文件名编码
            zipFile.setCharset(Charset.forName("GBK"));
            // 验证文件有效性
            if (!zipFile.isValidZipFile()) {
                throw new BizException(String.format(MessageEnum.FILE_TYPE_INVALID.getMessage(), ".zip"));
            }
            // 解压目录
            File fileDir = new File(unzipPath);
            // 目录不存在则创建
            if (fileDir.isDirectory() && !fileDir.exists()) {
                fileDir.mkdir();
            }
            // 解压
            zipFile.extractAll(unzipPath);
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

    public File createFile(File destinationDir, ZipEntry zipEntry) throws IOException {
        File destFile = new File(destinationDir, zipEntry.getName());
        String destDirPath = destinationDir.getCanonicalPath();
        String destFilePath = destFile.getCanonicalPath();

        if (!destFilePath.startsWith(destDirPath + File.separator)) {
            throw new IOException("Entry is outside of the target dir: " + zipEntry.getName());
        }
        return destFile;
    }

    public void moveFile(String filePath, String writePath, String fileName) {
        File file = new File(filePath);
        // 判断路径，没有创建
        File folder = new File(writePath);
        if (!folder.isDirectory()) {
            folder.mkdirs();
        }

        file.renameTo(new File(folder + File.separator + fileName));
    }

    /**
     * 为路径追加日期
     *
     * @param filePath 原始文件路径
     * @return 追加后的文件路径
     */
    public String appendDateDir(String filePath) {
        String dateStr = TimeFormatUtil.getDateStr(LocalDate.now());
        return filePath + dateStr + File.separator;
    }

    @Override
    public void close() {
        logger.info("自动关闭文件流");
    }

    public static FileHelper instance() {
        if (!Assert.notEmpty(FILE_HELPER)) {
            FILE_HELPER = new FileHelper();
        }
        return FILE_HELPER;
    }

    // 正则化URL路径
    public static String normalizeUrl(String path) {
        String placeholder = "6445BF55-95C4-41F9-8235-34CE3ED9146D";
        String result = path.replaceAll("\\\\+", "/");
        result = result.replaceAll("/+", "/");
        result = result.replaceAll("http:/+", placeholder);
        return result.replace(placeholder, "http://");
    }

    public String createFolder(String dir) throws IOException {
        return Files.createDirectory(Paths.get(dir)).toString();
    }

    public void copyFile(String pathStr, String dest, String fileName) {
        try {
            // 判断路径，没有创建
            File folder = new File(dest);
            if (!folder.isDirectory()) {
                folder.mkdirs();
            }
            Files.copy(Paths.get(pathStr), Paths.get(dest + File.separator + fileName));
        } catch (Exception e) {
            logger.error("[copyFile exception:] {}", E2s.exception2String(e));
            if (e instanceof NoSuchFileException) {
                throw new BizException(MessageEnum.NoSuchFileException.getMessage());
            }

            throw new BizException(MessageEnum.IO_EXCEPTION.getMessage());
        }
    }

    public FileDTO WriteToFile(String filename, String filePath, String content, boolean append) {
        FileWriter fileWriter;
        FileDTO fileDTO = new FileDTO();
        fileDTO.setName(filename);
        fileDTO.setFileExtension(".txt");

        try {
            // 判断路径，没有创建
            File folder = new File(filePath + filename);
            if (!folder.exists()) {
                folder.createNewFile();
            }
            fileWriter = new FileWriter(filePath + filename, append);
            fileWriter.write(content);
            fileWriter.flush();
            fileWriter.close();
            fileDTO.setFileSize(folder.length());
            fileDTO.setFilePath(filePath + filename);
        } catch (IOException e) {
            e.printStackTrace();
            throw new BizException(MessageEnum.EXCEPTION.getMessage());
        }
        return fileDTO;
    }

    public String readToString(String fileName) {
        String encoding = "UTF-8";
        File file = new File(fileName);
        long fileLength = file.length();
        byte[] fileContent = new byte[(int) fileLength];
        try {
            FileInputStream in = new FileInputStream(file);
            in.read(fileContent);
            in.close();
        } catch (Exception e) {
            e.printStackTrace();
            throw new BizException(MessageEnum.EXCEPTION.getMessage());
        }
        try {
            return new String(fileContent, encoding);
        } catch (UnsupportedEncodingException e) {
            System.err.println("The OS does not support " + encoding);
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 存储字节流文件
     *
     * @param fileBytes 文件字节流
     * @param filePath  文件路径
     */
    public void saveFileBytes(byte[] fileBytes, String filePath) throws Exception {
        if (Assert.notEmpty(fileBytes)) {
            FileOutputStream out = new FileOutputStream(filePath);
            out.write(fileBytes);
            out.close();
        }
    }

    public static <T> T readJsonFile(String path, TypeReference<T> valueTypeRef) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        File file = new File(path);
        return mapper.readValue(file, valueTypeRef);
    }
}
